package org.bjf.modules.core.web.advice;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.bjf.exception.BaseException;
import org.bjf.exception.FatalException;
import org.bjf.exception.MsgCode;
import org.bjf.modules.core.web.core.LiveResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;

import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.bjf.exception.CommMsgCode.*;

/**
 * 全局异常处理
 */

@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {

    private static final Logger logEmail = LoggerFactory.getLogger("email");

    /**
     * 405 not support异常
     */
    @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class})
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public LiveResp onNotSupportedException(Exception e,
                                            HttpServletRequest request) {
        String uri = request.getRequestURI();
        log.error("uri:{},code:{},message:{}", uri, NOT_SUPPORTED.getCode(), e.getMessage());

        return createErrorResp(NOT_SUPPORTED, null);
    }

    /**
     * 404 not support异常
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public LiveResp onException(NoHandlerFoundException e, HttpServletRequest request) {
        String uri = request.getRequestURI();
        log.error("uri:{},code:{},message:{}", uri, NOT_FOUND.getCode(), e.getMessage());

        return createErrorResp(NOT_FOUND, null);
    }
    /**
     * 401 saToken登录验证不通过
     */
    @ExceptionHandler(value = NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public LiveResp onException(NotLoginException e, HttpServletRequest request) {
        String uri = request.getRequestURI();
        log.error("uri:{},code:{},message:{}", uri, UNAUTHORIZED.getCode(), e.getMessage());

        return createErrorResp(UNAUTHORIZED, null);
    }
    /**
     * 403 saToken权限验证不通过
     */
    @ExceptionHandler(value = NotPermissionException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public LiveResp onException(NotPermissionException  e, HttpServletRequest request) {
        String uri = request.getRequestURI();
        log.error("uri:{},code:{},message:{}", uri, FORBIDDEN.getCode(), e.getMessage());

        return createErrorResp(FORBIDDEN, null);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public LiveResp onException(Exception e, HttpServletRequest request) {
        String uri = request.getRequestURI();
        String params = JSON.toJSONString(request.getParameterMap());

        if (e instanceof SQLException || e instanceof DataAccessException) {
            createLog(e, uri, params);
            return createErrorResp(DB_ERROR, null);
        } else if (e instanceof FatalException) {
            FatalException ex = (FatalException) e;
            // 异步发送邮件
            logEmail.error(ex.getMessage(), ex);
            createLog(e, uri, params);
            return createErrorResp(ex.getMsgCode(), ex.getMessage());
        } else if (e instanceof BaseException) {
            BaseException ex = (BaseException) e;
            log.error("uri:{},params:{},code:{},message:{}", uri, params, ex.getMsgCode().getCode(),
                    ex.getMessage());
            return createErrorResp(ex.getMsgCode(), ex.getMessage());
        } else if (e instanceof MissingServletRequestParameterException
                || e instanceof BindException
                || e instanceof ConstraintViolationException
                || e instanceof TypeMismatchException
                || e instanceof ServletRequestBindingException) {
            createLog(e, uri, params);
            return createErrorResp(PARAM_ERROR, null);
        } else {
            createLog(e, uri, params);
            return createErrorResp(SERVER_ERROR, null);
        }
    }

    private LiveResp createErrorResp(MsgCode msgCode, String message) {
        return new LiveResp(msgCode.getCode(), isNotBlank(message) ? message : msgCode.getMessage());
    }

    private void createLog(Exception e, String uri, String params) {
        log.error("uri:" + uri + ",params:" + params, e);
    }

}
